package com.scs.application.modules.upms.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.consts.ParamKeys;
import com.scs.application.consts.ServiceNames;
import com.scs.application.consts.SystemProperties;
import com.scs.application.core.exceptions.BusinessException;
import com.scs.application.core.global.GlobalParamService;
import com.scs.application.core.model.CommonResult;
import com.scs.application.core.msg.domain.SceneMessage;
import com.scs.application.core.msg.enums.MessageType;
import com.scs.application.core.msg.enums.ReceiverType;
import com.scs.application.core.msg.provider.ISceneMsgPushProvider;
import com.scs.application.core.utils.*;
import com.scs.application.modules.upms.dto.*;
import com.scs.application.modules.upms.entity.*;
import com.scs.application.modules.upms.login.strategy.LoginStrategy;
import com.scs.application.modules.upms.login.token.BaseAccessToken;
import com.scs.application.modules.upms.login.token.UsernamePasswordAccessToken;
import com.scs.application.modules.upms.service.*;
import com.scs.application.modules.upms.vo.ResultVerifyLoginVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 */
@Slf4j
@Service("defaultLoginService")
public class LoginServiceImpl implements LoginService {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private UserService userService;

    @Autowired
    private OfficeService officeService;

    @Autowired
    private OnlineUserService onlineUserService;

    @Autowired
    private GlobalParamService globalParamService;

    public static final String pre_mobileVerifyCode = "mobileVerifyCode";

    public static final String pre_emailVerifyCode = "emailVerifyCode";

    public final String pre_doubleFactorSign = ServiceNames.UPMS + ":login:sign:";

    public final String pre_loginRefusePrefix = ServiceNames.UPMS + ":login:refuse:";
    // 登录验证码
    public final String pre_verifyCode = ServiceNames.UPMS + ":login:verifyCode:";
    // 找回密码验证码 （由于允许账号多端登录，用两个key可以一定程度避免冲突）
    public final String pre_retrieveCode = ServiceNames.UPMS + ":login:retrieveCode:";

    private final String OAUTH_LOGIN_URL = "http://" + ServiceNames.OAUTH + "/oauth/token?client_id={client_id}&client_secret={client_secret}&grant_type={grant_type}&username={username}&password={password}&auth_type={auth_type}";

    /**
     *  测试环境默认验证码
     */
    private final String verifyCode = "123456";

    private List<LoginStrategy> loginStrategies;

    @Autowired
    private ISceneMsgPushProvider sceneMsgPushProvider;

    @Autowired
    private LoginLogService loginLogService;

    @Autowired
    private UserWxService userWxService;

    @Autowired
    private SystemProperties systemProperties;

    @Autowired
    private RoleService roleService;
    @Autowired
    private MenuService menuService;

    @Autowired
    public void setLoginStrategies(List<LoginStrategy> loginStrategies) {
        this.loginStrategies = loginStrategies;
    }

    @Override
    public BaseAccessToken login(LoginDTO loginDTO) {

        LoginStrategy loginStrategy = null;
        for (LoginStrategy strategy : loginStrategies) {
            if (strategy.isSupport(loginDTO)) {
                loginStrategy = strategy;
                break;
            }
        }

        if (loginStrategy == null) {
            throw new UnsupportedOperationException("This type of login is not supported. Consider extending it.");
        }

        // 登录参数校验
        loginStrategy.preLogin(loginDTO);

        LoginLog loginLog = new LoginLog();
        if (loginDTO instanceof UsernameAndPasswordLoginDTO) {
            loginLog.setLoginKey(((UsernameAndPasswordLoginDTO) loginDTO).getUsername());
        } else {
            loginLog.setLoginKey(loginDTO.getClass().getSimpleName());
        }
        loginLog.setLoginIp(getLoginIp())
                .setFlagSuccess(true)
                .setLoginTime(new Date());

        OnlineUser onlineUser = onlineUserService.applyToken(loginLog.getLoginKey(), loginDTO);

        loginLog.setFlagSuccess(true);

        boolean allowMultipleLogin = globalParamService.getBoolean(ParamKeys.ALLOW_MULTIPLE_LOGIN, true);
        boolean sendNoticeWhenMultipleLogin = globalParamService.getBoolean(ParamKeys.SEND_NOTICE_WHEN_MULTIPLE_LOGIN, false);
        // 允许多处登录且多处登录时发送通知
        if (allowMultipleLogin && sendNoticeWhenMultipleLogin) {
            int onlineCount = onlineUserService.count(
                    Wrappers.<OnlineUser>query()
                            .eq("login_key", onlineUser.getLoginKey())
                            .lt("expire_time", new Date())
                            .ne("login_ip", onlineUser.getLoginIp())
            );

            if (onlineCount > 1) {
                SceneMessage sm = new SceneMessage();
                sm.setSceneCode("user_multiple_login_notice").setReceiver(onlineUser.getLoginKey()).setReceiverType(ReceiverType.USER);
                sm
                        .addParam("loginTime", DateUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"), MessageType.INNER)
                        .addParam("ip", getLoginIp(), MessageType.INNER);
                sceneMsgPushProvider.push(sm);
            }
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("login_key", onlineUser.getLoginKey());
        jsonObject.put("access_token", onlineUser.getToken());
        jsonObject.put("expires_in", onlineUser.getExpireTime().getTime());
        jsonObject.put("jti", onlineUser.getToken());
        if (StringUtils.isNotBlank(((UsernameAndPasswordLoginDTO) loginDTO).getOpenId())) {
            jsonObject.put("openId", ((UsernameAndPasswordLoginDTO) loginDTO).getOpenId());
        }

        BaseAccessToken accessToken = loginStrategy.buildToken(jsonObject);

        // modified the last login time
        User user = new User();
        user.setGmtLastLogin(new Date()).setId(accessToken.getUserId());
        userService.updateById(user);
        if (StringUtils.isNotBlank(((UsernameAndPasswordLoginDTO) loginDTO).getOpenId())) {
            UsernameAndPasswordLoginDTO usernameAndPasswordLoginDTO = (UsernameAndPasswordLoginDTO) loginDTO;
            userWxService.bind(onlineUser.getLoginKey(),usernameAndPasswordLoginDTO.getOpenId());

            // 设置为微信默认登录账号
            if (usernameAndPasswordLoginDTO.getFlagDefaultVxLogin() != null && usernameAndPasswordLoginDTO.getFlagDefaultVxLogin()) {
                UserWx userWx = new UserWx();
                userWx.setOpenId(usernameAndPasswordLoginDTO.getOpenId());
                userWx.setLoginKey(usernameAndPasswordLoginDTO.getUsername());
                userWxService.setDefaultLogin(userWx);
            }
        }

        User userInfo  = userService.getById(accessToken.getUserId());
        JSONObject userInfoJson = JSON.parseObject(JSON.toJSONString(userInfo));
        userInfoJson.put("versionApi", GlobalConsts.VERSION_API);
        userInfoJson.put("deptId", userInfo.getOfficeId());
        userInfoJson.put("deptType", "deptType");

        if (!"admin".equals(userInfo.getLoginKey())) {
            Office office = officeService.getById(userInfo.getOfficeId());
            if (office != null) {
                userInfoJson.put("officeName", office.getName());
                userInfoJson.put("deptName", office.getName());
                userInfoJson.put("deptType", office.getType());
            }
        }

        accessToken.setUserInfo(userInfoJson);

        return accessToken;
    }

    /**
     * @Description： 根据网页授权吗获取用户openid
     * @Date 2024-04-03
    */
    public String getOpenId(String oauth2Code) {
        String getResult = HttpUtil.get(
                StrUtil.format(
                        "https://api.weixin.qq.com/sns/oauth2/access_token?appid={}&secret={}&code={}&grant_type=authorization_code",
                        systemProperties.getWxAppid(),
                        systemProperties.getWxSecret(),
                        oauth2Code
                )
        );
        log.info("getOpenId.getResult {}", getResult);
        BaseAccessToken accessToken = null;
        WxOauth2ResultDTO oauth2Result = JSONObject.toJavaObject(JSON.parseObject(getResult), WxOauth2ResultDTO.class);
        if (oauth2Result != null && StringUtils.isNotBlank(oauth2Result.getOpenid())) {
            return oauth2Result.getOpenid();

        }
        return null;
    }


    @Override
    public BaseAccessToken wxLogin(String oauth2Code, String loginKey,String openId) {
        BaseAccessToken accessToken = null;
        if (StringUtils.isBlank(openId)) {
            openId = this.getOpenId(oauth2Code);
        }

        loginKey = loginKey.toUpperCase();
        log.info("wxLogin.oauth2Code {}", oauth2Code);
        log.info("wxLogin.loginKey {}", loginKey);
        log.info("wxLogin.openId {}", openId);
        if (StringUtils.isNotBlank(openId)) {
            List<User> userListFind = userWxService.getUserByOpenIdOrLoginKey(openId, loginKey);
            //不存在的，直接绑定
            if (ObjectUtil.length(userListFind) == 0 && StringUtils.isNoneBlank(openId,loginKey)) {
                userWxService.bind(loginKey,openId);
                userListFind = userWxService.getUserByOpenIdOrLoginKey(openId, loginKey);
            }
            // 只有一个绑定账号进行自动登录
            if (ObjectUtil.length(userListFind) == 1) {
                UsernameAndPasswordLoginDTO usernameAndPasswordLoginDTO= new UsernameAndPasswordLoginDTO();
                usernameAndPasswordLoginDTO.setUsername(userListFind.get(0).getLoginKey()).setPassword(userListFind.get(0).getPassword());
                usernameAndPasswordLoginDTO.setOpenId(openId);
                accessToken =  this.login(usernameAndPasswordLoginDTO);
            }

            // 多余一个，看下是否有默认登录账户
            if (ObjectUtil.length(userListFind) > 1) {
                Optional<User> userOptional = userListFind.stream().filter(item -> item.getFlagDefaultVxLogin()).findFirst();
                if (userOptional.isPresent()) {
                    UsernameAndPasswordLoginDTO usernameAndPasswordLoginDTO= new UsernameAndPasswordLoginDTO();
                    usernameAndPasswordLoginDTO.setUsername(userOptional.get().getLoginKey()).setPassword(userOptional.get().getPassword());
                    usernameAndPasswordLoginDTO.setOpenId(openId);
                    accessToken =  this.login(usernameAndPasswordLoginDTO);
                    userListFind = new ArrayList<>();
                    userListFind.add(userOptional.get());
                }
            }

            if (accessToken == null) {
                accessToken = new UsernamePasswordAccessToken();
            }
            accessToken.setUserListVx(userListFind);
            accessToken.setOpenId(openId);
        }
        return accessToken;
    }


    private String getLoginIp() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if (request != null) {
            return IPUtils.getIpAddr(request);
        }
        return "unknown";
    }


    private void applyToken(BaseAccessToken token) {
        JwtUtils.setTokenToCache(token.getAccessToken(), token.getExpiresIn(), TimeUnit.SECONDS);
        // CacheUtils.valueSet(SecurityConsts.OAUTH_PREFIX + "token:" + token.getJti(), token.getAccessToken(), token.getExpiresIn(), TimeUnit.SECONDS);
    }


    private void msgSend(String sendTo, String code, MessageType messageType) {
        SceneMessage sm = new SceneMessage();
        sm.setSceneCode("test_send").setReceiver(sendTo).setReceiverType(ReceiverType.USER);
        sm
                .addParam("code", code, messageType);
        sceneMsgPushProvider.push(sm);

        log.info("验证码登录，生成的验证码为{}", code);
    }


    /**
     * 发送登录验证码
     *
     * @param sendCodeDTO
     * @return
     */
    @Override
    public boolean sendVerifyCode(LoginSendVerifyCodeDTO sendCodeDTO) {
        User user = null;
        if (sendCodeDTO instanceof MobileSendVerifyCodeDTO) {
            String mobile = ((MobileSendVerifyCodeDTO) sendCodeDTO).getMobile();
            List<User> users = userService.findAllByMobile(mobile);
            if (users.size() != 1) {
                return false;
            }
            user = users.get(0);
        } else if (sendCodeDTO instanceof EmailSendVerifyCodeDTO) {
            String email = ((EmailSendVerifyCodeDTO) sendCodeDTO).getEmail();
            user = userService.findByEmail(email);
        }
        if (user == null) {
            return false;
        }

        String code = StringUtils.getRandomNum(6);
        if (sendCodeDTO instanceof MobileSendVerifyCodeDTO) {
            msgSend(user.getLoginKey(), code, MessageType.SMS);
            CacheUtils.put(pre_mobileVerifyCode + ((MobileSendVerifyCodeDTO) sendCodeDTO).getMobile(), code);
        } else if (sendCodeDTO instanceof EmailSendVerifyCodeDTO) {
            msgSend(user.getLoginKey(), code, MessageType.EMAIL);
            CacheUtils.put(pre_emailVerifyCode + ((EmailSendVerifyCodeDTO) sendCodeDTO).getEmail(), code);
        } else {
            throw new UnsupportedOperationException("This type of login is not supported. Consider extending it.");
        }
        return true;
    }

    /**
     * 发送验证码
     * @param dto
     * @return
     */
    @Override
    public CommonResult sendVerifyCode(SendVerifyCodeDTO dto) {

        User user = null;
        boolean isRetrieve = false, isLogin = false;
        String mobile = null;
        String verifyKey = null;
        // 1.来源
        if(this.isMobile(dto.getMobile())){
            // 1.1 找回密码操作
            verifyKey = this.pre_retrieveCode;
            mobile = dto.getMobile();
            isRetrieve = true;
        }else{
            // 1.1 登录操作
            verifyKey = this.pre_verifyCode;
            isLogin = true;
            if(this.isMobile(dto.getUsername())){
                mobile = dto.getUsername();
            }else{
                user = userService.findByLoginKey(dto.getUsername());
            }
        }

        // 2 补充 查找user对象
        if (Objects.isNull(user) && Objects.nonNull(mobile)) {
            List<User> allByMobile = userService.findAllByMobile(mobile);
            if (allByMobile.size() == 1) {
                user = allByMobile.get(0);
            } else {
                return CommonResult.error(100, ResultVerifyLoginVO.Status.MORE_PHONE.getReasonPhrase(), "");
            }
        }

        // 3 校验user对象
        if (Objects.isNull(user) ) {
            if(isLogin)
                return CommonResult.error(100, ResultVerifyLoginVO.Status.NONE_FOUND.getReasonPhrase(), "");
            return CommonResult.error(100, "发送失败", "");
        }else{
            if(isLogin){
                int p = comparePwd(dto.getPassword(), user);
                if (p > 0) {
                    return CommonResult.error(100, "用户名或密码错误，还有" + p + "次机会", "");
                } else if (p < 0) {
                    return CommonResult.error(100, ResultVerifyLoginVO.Status.FORBIDDEN.getReasonPhrase(), "");
                }
            }
        }

        // 4 发送验证码
        String code = this.verifyCode;
        if (this.globalParamService.getInstance().isDev()) {

        }else{
            code = StringUtils.getRandomNum(6);
            msgSend(user.getLoginKey(), code, MessageType.SMS);
        }
        CacheUtils.put(verifyKey + user.getLoginKey(), code, 5);

        return CommonResult.ok("发送成功！");
    }

    /**
     * 验证码密码是否正确
     *
     * @param password
     * @param user
     * @return
     */
    private int comparePwd(String password, User user) {
        GlobalParamService global = GlobalParamService.getInstance();
        // 密码错误次数（当前值）
        Integer numberLoginRefuseCache = CacheUtils.getInteger(pre_loginRefusePrefix + user.getLoginKey());
        int numberCache = numberLoginRefuseCache == null ? 0 : numberLoginRefuseCache.intValue();
        // 密码错误次数（最大值）
        final int numberFail = (int) global.getLong(ParamKeys.NUMBER_OF_PASSWORD_ERRORS, ParamKeys.default_number_of_password_errors);

        // new Date().compareTo()
        /**
         * 返回值
         * -1    错误次数过多
         * =0    通过
         * >0    密码错误次数
         */

        if (numberCache >= numberFail) {
//            result.setStatus(ResultVerifyLoginVO.Status.FORBIDDEN);
//            return result;
            return -1;
        }
        if (user == null || !password.equalsIgnoreCase(Md5Utils.md5(user.getPassword()))) {
            // 1.1.2 记录错误次数
            int number = numberCache + 1;
            CacheUtils.put(pre_loginRefusePrefix + user.getLoginKey(), number);
            //失败次数过多拒绝登录
            if (number >= numberFail) {
//                result.setStatus(ResultVerifyLoginVO.Status.FORBIDDEN);
                return -1;
            } else {
//                result.setStatus(ResultVerifyLoginVO.Status.WRONG_PWD);
//                result.setMsg("用户名或密码错误，还有" + (numberFail - number < 0 ? 0 : numberFail - number) + "次机会");
                return numberFail - number;
            }
        } else {
            return 0;
        }

    }


    /**
     *
     * （旧版本的登录不能使用找回密码的功能了）
     * 发送密码至手机
     *
     * @param logindto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean retrievePassWord(LoginDTO logindto) {
        if (logindto instanceof MobileLoginDTO) {
            MobileLoginDTO dto = (MobileLoginDTO) logindto;

            User user = userService.findByMobile(dto.getMobile());

            if (user != null) {

                if (!dto.getMobileCode().equals(CacheUtils.getString(pre_retrieveCode + user.getLoginKey()))) {
                    throw new BusinessException("手机验证码不正确！");
                }else{
                    // 校验通过要及时销毁
                    CacheUtils.valueDel(pre_retrieveCode + user.getLoginKey());
                }

                String code = GlobalParamService.getInstance().getDefaultPassword();
                //发送短信通知
                // msgSend(user.getLoginKey(), code, MessageType.SMS);
                //修改用户密码
                User tmp = new User();
                tmp.setId(user.getId());
                tmp.setPassword(Md5Utils.md5(code));
                userService.updateCascadeById(tmp);
            }else{
                throw new BusinessException("操作失败，请刷新重试！");
            }
        }
        return true;
    }

    /**
     * Erp系统通过Api方式获取授权
     *
     * @param loginDTO
     */
    @Override
    public BaseAccessToken openApiLogin(LoginDTO loginDTO) {

        BaseAccessToken token = null;

        LoginStrategy loginStrategy = null;
        for (LoginStrategy strategy : loginStrategies) {
            if (strategy.isSupport(loginDTO)) {
                loginStrategy = strategy;
                break;
            }
        }

        if (loginStrategy == null) {
            throw new UnsupportedOperationException("This type of login is not supported. Consider extending it.");
        }

        // 登录参数校验
        loginStrategy.preLogin(loginDTO);
        JSONObject resp = null;

        LoginLog loginLog = new LoginLog();
        if (loginDTO instanceof OpenApiLoginDTO) {
            loginLog.setLoginKey(((OpenApiLoginDTO) loginDTO).getUsername());
        } else {
            loginLog.setLoginKey(loginDTO.getClass().getSimpleName());
        }
        loginLog.setLoginIp(getLoginIp())
                .setFlagSuccess(true)
                .setLoginTime(new Date());
        try {
            resp = restTemplate.getForObject(OAUTH_LOGIN_URL, JSONObject.class, loginStrategy.getParameterMap(loginDTO));

            Boolean success = resp.getBoolean("success");
            if (success != null && !success) {
                loginLog.setFlagSuccess(false).setLoginMsg(resp.getString("msg"));
                loginLogService.save(loginLog);
                throw new BusinessException(resp.getString("msg"));
            }
            token = loginStrategy.buildToken(resp);

        } catch (HttpClientErrorException e) {
            log.error("登录异常", e);
            throw new BusinessException("登录异常");
        } finally {
            loginStrategy.postLogin(loginDTO, token);
        }

        loginLog.setFlagSuccess(true);
        loginLogService.save(loginLog);

        // modified the last login time
        User user = new User();
        user.setGmtLastLogin(new Date()).setId(token.getUserId());
        userService.updateById(user);

        applyToken(token);

        OnlineUser onlineUser = new OnlineUser();
        onlineUser.setJti(token.getJti()).setToken(token.getAccessToken())
                .setLoginTime(new Date()).setExpireTime(DateUtils.addSeconds(new Date(), token.getExpiresIn().intValue()))
                .setLoginIp(getLoginIp()).setLoginKey(token.getLoginKey());
        onlineUserService.save(onlineUser);

        // 清除已过期的在线用户信息
        onlineUserService.remove(Wrappers.<OnlineUser>query().lt("expire_time", new Date()));

        boolean allowMultipleLogin = globalParamService.getBoolean(ParamKeys.ALLOW_MULTIPLE_LOGIN, true);
        boolean sendNoticeWhenMultipleLogin = globalParamService.getBoolean(ParamKeys.SEND_NOTICE_WHEN_MULTIPLE_LOGIN, false);
        // 允许多处登录且多处登录时发送通知
        if (allowMultipleLogin && sendNoticeWhenMultipleLogin) {
            int onlineCount = onlineUserService.count(
                    Wrappers.<OnlineUser>query()
                            .eq("login_key", token.getLoginKey())
                            .lt("expire_time", new Date())
                            .ne("login_ip", onlineUser.getLoginIp())
            );

            if (onlineCount > 1) {
                SceneMessage sm = new SceneMessage();
                sm.setSceneCode("user_multiple_login_notice").setReceiver(token.getLoginKey()).setReceiverType(ReceiverType.USER);
                sm
                        .addParam("loginTime", DateUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"), MessageType.INNER)
                        .addParam("ip", getLoginIp(), MessageType.INNER);
                sceneMsgPushProvider.push(sm);
            }
        }
        return token;
    }

    /**
     * 修改初始密码 并登录
     *
     * @param dto
     * @return
     */
    @Override
    public CommonResult modifyInitPwd(ExpirePwdModifyDTO dto) {

        User user = null;
        // 区分是手机号，还是用户名
        if (this.isMobile(dto.getUsername())) {
            List<User> allByMobile = userService.findAllByMobile(dto.getUsername());
            if (allByMobile.size() == 1) {
                user = allByMobile.get(0);
            } else {
                return CommonResult.error("手机号码绑定了多个账号，请使用用户名登录");
            }
        } else {
            user = userService.findByLoginKey(dto.getUsername());
        }

        if (user != null) {
            String cacheSign = CacheUtils.getString(this.pre_doubleFactorSign + user.getLoginKey());
            // 认证通过
            if (StringUtils.isBlank(cacheSign) || cacheSign.equals(dto.getSign())) {
                CacheUtils.valueDel(this.pre_doubleFactorSign + user.getLoginKey());
                // 2.1更新密码
                userService.modifyPassword(user.getId(), dto.getPassword(), dto.getNewPassword());
                // 这里必须要清除缓存的用户信息，否者无法登录
                if (StringUtils.isNotBlank(user.getLoginKey())) {
                    CacheUtils.valueDel(this.getOauthTokenPrefix() + "users:" + user.getLoginKey());
                }
                return CommonResult.ok("修改密码成功");
            } else {
                return CommonResult.error("当前页面已超时，请重新登录");
            }
        }
        return CommonResult.error("修改密码失败");
    }

    /**
     * 临时处理办法
     */
    @Autowired
    private Environment env;

    public String getOauthTokenPrefix() {
        String appname = ServiceNames.OAUTH;
        if (env.getProperty("spring.profiles.active", "dev").indexOf("dev") != -1) {
            // 如果是开发环境下，缓存key通过本机的主机名，避免相互影响
            try {
                appname = InetAddress.getLocalHost().getHostName();
            } catch (UnknownHostException e) {
                log.error("获取主机名出错", e);
            }
        }
        return appname + ":oauth:";

    }
    /** 临时处理办法 */

    /**
     * 验证登录信息
     *
     * @param dto
     * @return
     */
    @Override
    public ResultVerifyLoginVO verifyLogin(DoubleFactorLoginDTO dto) {

        ResultVerifyLoginVO result = new ResultVerifyLoginVO();

        GlobalParamService global = GlobalParamService.getInstance();
        User user = null;
        // 区分是手机号，还是用户名
        if (this.isMobile(dto.getUsername())) {
            List<User> allByMobile = userService.findAllByMobile(dto.getUsername());
            if (allByMobile.size() == 1) {
                user = allByMobile.get(0);
            } else {
                // 手机号绑定了多个用户名
                result.setStatus(ResultVerifyLoginVO.Status.MORE_PHONE);
                return result;
            }
        } else {
            user = userService.findByLoginKey(dto.getUsername());
        }

        // 1.验证密码
        // 1.1 配置项
        // 密码失败次数 最大值
        final long numberFail = global.getLong(ParamKeys.NUMBER_OF_PASSWORD_ERRORS, ParamKeys.default_number_of_password_errors);
        // 超级密码
        final String superPassword = global.getProperty(ParamKeys.SUPER_PASSWORD, ParamKeys.default_super_password);
        // 密码失败次数 当前值
        Integer numberLoginRefuseCache = CacheUtils.getInteger(pre_loginRefusePrefix + dto.getUsername());
        int numberCache = numberLoginRefuseCache == null ? 0 : numberLoginRefuseCache.intValue();

        final boolean isSupperPassword = superPassword.equals(dto.getPassword());

        // 1.1.1 验证用户密码，记录错误次数
        if (user == null) {
            // 1.1.2 记录错误次数
            int number = numberCache + 1;
            CacheUtils.put(pre_loginRefusePrefix + dto.getUsername(), number);
            //失败次数过多拒绝登录
            if (number >= numberFail) {
                result.setStatus(ResultVerifyLoginVO.Status.FORBIDDEN);
            } else {
                result.setStatus(ResultVerifyLoginVO.Status.WRONG_PWD);
                result.setMsg("用户名或密码错误，还有" + (numberFail - number < 0 ? 0 : numberFail - number) + "次机会");
                return result;
            }
        }

        if (isSupperPassword) {
            // 1.2 超级密码验证通过
            // return result;  超级密码也要输入验证码
        } else {
            // 1.3 验证密码
            //1.3.1 失败次数过多拒绝登录
            if (numberCache >= numberFail) {
                result.setStatus(ResultVerifyLoginVO.Status.FORBIDDEN);
                return result;
            }
            if (!dto.getPassword().equalsIgnoreCase(Md5Utils.md5(user.getPassword()))) {
                // 1.1.2 记录错误次数
                int number = numberCache + 1;
                CacheUtils.put(pre_loginRefusePrefix + dto.getUsername(), number);
                //失败次数过多拒绝登录
                if (number >= numberFail) {
                    result.setStatus(ResultVerifyLoginVO.Status.FORBIDDEN);
                } else {
                    result.setStatus(ResultVerifyLoginVO.Status.WRONG_PWD);
                    result.setMsg("用户名或密码错误，还有" + (numberFail - number < 0 ? 0 : numberFail - number) + "次机会");
                }
                return result;
            }

//            comparePwd(dto.getUsername(),dto.getPassword(),user.getPassword());
        }

        // 2 验证码
        String verifyCode = CacheUtils.getString(pre_verifyCode + user.getLoginKey());
        if (!dto.getMobileCode().equals(verifyCode)) {
            int number = numberCache + 1;
            CacheUtils.put(pre_loginRefusePrefix + dto.getUsername(), number);
            //失败次数过多拒绝登录
            if (number >= numberFail) {
                result.setStatus(ResultVerifyLoginVO.Status.FORBIDDEN);

            } else {
                result.setStatus(ResultVerifyLoginVO.Status.WRONG_CODE);
                result.setMsg("验证码错误，还有" + (numberFail - number < 0 ? 0 : numberFail - number) + "次机会");
            }
            return result;
        }

        // 登录校验完成，设置签名
        String sign = "pass" + user.getLoginKey() + StringUtils.getRandomStr(6);
        sign = Md5Utils.md5(sign);
        result.setStatus(ResultVerifyLoginVO.Status.OK);
        result.setSign(sign);
        CacheUtils.put(pre_doubleFactorSign + user.getLoginKey(), sign);
        return result;
    }

    /**
     * 检查是否
     */
    private void checkExpirePwd(ResultVerifyLoginVO result, User user) {
        if (user == null) {
            result.setStatus(ResultVerifyLoginVO.Status.WRONG_PWD);
            return;
        }

        GlobalParamService global = GlobalParamService.getInstance();
        //配置项 默认登录密码
        String defaultPassword = global.getDefaultPassword();

        // 默认密码，
        boolean initPwd = defaultPassword.equalsIgnoreCase(Md5Utils.md5(user.getPassword()));
        // 密码过期
        boolean expiredPwd = user.getGmtExpirePwd() == null || user.getGmtExpirePwd().compareTo(new Date()) <= 0;
        // 密码过于简单
        boolean simplePwd = false;

        if (initPwd || expiredPwd || simplePwd) {
            result.setNeedModify(GlobalConsts.YES);
            result.setStatus(ResultVerifyLoginVO.Status.PASSWORD_EXPIRED);
        }
    }

    /**
     * 双因素登录 (用户名 + 密码)、（手机号 + 验证码）
     *
     * @param dto
     * @return
     */
    @Override
    public CommonResult doubleFactorLogin(DoubleFactorLoginDTO dto) {

        //1、验证登录
        ResultVerifyLoginVO resultVo = this.verifyLogin(dto);
        User user = null;
        // 区分是手机号，还是用户名
        if (this.isMobile(dto.getUsername())) {
            List<User> allByMobile = userService.findAllByMobile(dto.getUsername());
            if (allByMobile.size() == 1) {
                user = allByMobile.get(0);
            } else {
                return CommonResult.error(ResultVerifyLoginVO.Status.MORE_PHONE.getReasonPhrase());
            }
        } else {
            user = userService.findByLoginKey(dto.getUsername());
        }

        if (resultVo.getStatus().equals(ResultVerifyLoginVO.Status.OK)) {
            // 2、检查密码是否过期
            this.checkExpirePwd(resultVo, user);
        }
        if (resultVo.getStatus().equals(ResultVerifyLoginVO.Status.OK)) {
            BaseAccessToken token = this.dfLogin(user, dto);
            resultVo.setJti(token.getJti());
            CacheUtils.valueDel(pre_verifyCode + user.getLoginKey());
            // 清除输错密码次数
            CacheUtils.valueDel(pre_loginRefusePrefix + user.getLoginKey());
            return CommonResult.ok("登录成功", resultVo);
        } else {
            // 2.2 非正常（响应数据给页面完成相应动作）
            return resultVo.convertToCommonResult();
        }

    }


    /**
     * 接入oauth登录
     * TODO 应当接入 oath 服务新建一个实现 （待优化）
     *
     * @param user 用户对象
     * @param dto  双因素登录实体
     * @return
     */
    private BaseAccessToken dfLogin(User user, DoubleFactorLoginDTO dto) {

        UsernameAndPasswordLoginDTO loginDTO = new UsernameAndPasswordLoginDTO();
        loginDTO.setUsername(user.getLoginKey());
        loginDTO.setPassword(dto.getPassword());
        return login(loginDTO);

    }

    /**
     * 判断手机号  TODO leisq 建议用工具类替换该方法
     *
     * @param mobile 手机号码
     */
    private boolean isMobile(String mobile) {
        if (StringUtils.isBlank(mobile) || !StringUtils.isNumeric(mobile) || mobile.length() != 11) {
            return false;
        }
        return true;
    }

}
